package com.fanqwz.kotlinmvvm.common.util.rxjava.gson

import com.fanqwz.kotlinmvvm.common.util.log.loge
import com.google.gson.*
import com.google.gson.annotations.SerializedName
import java.lang.reflect.Field
import java.lang.reflect.Type
import kotlin.reflect.KClass
import kotlin.reflect.jvm.javaType

/**
 * Created by fanqwz on 2017/7/25.
 */

// 后端返回值不规范，空对象使用空的花括号，但是返回了空字符串，需要手动处理反序列化，并在实体类上使用注解
// 不能用于泛型类
class GsonNullDeserializer : JsonDeserializer<Any> {

    internal lateinit var mJsonPrimitiveValueField: Field

    init {
        try {
            mJsonPrimitiveValueField = JsonPrimitive::class.java.getDeclaredField("value")
            mJsonPrimitiveValueField.isAccessible = true
        } catch (e: NoSuchFieldException) {
            e.printStackTrace()
        }

    }

    @Throws(JsonParseException::class)
    override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Any? {
        if (json.isJsonObject && typeOfT is Class<*>/*泛型类此处为gson中的类*/) {
            val jsonObject = json.asJsonObject
            try {
                return typeOfT.newInstance().apply {
                    this.javaClass.declaredFields.forEach { itr ->
                        val element = itr.getAnnotation(SerializedName::class.java)?.let { ano -> jsonObject.get(ano.value) }
                                ?: jsonObject.get(itr.name)
                        element?.let {
                            itr.isAccessible = true
                            try {
                                itr.set(this, context.deserialize(it, itr.genericType))
                            } catch (e: Exception) {
                                e.printStackTrace()
                                loge("deserialize: class:$this,field:$it", e)
                            }
                        }
                    }
                }
            } catch (e: InstantiationException) {
                if (typeOfT.kotlin.isData)
                    return deserializeKotlinDataClass(json, jsonObject, typeOfT.kotlin, context)
                loge("deserialize: ", e)
            } catch (e: IllegalAccessException) {
                loge("deserialize: ", e)
            }

        }

        loge("deserialize: parse type $typeOfT return null, json is $json")
        return null
    }

    private fun deserializeKotlinDataClass(json: JsonElement, jsonObject: JsonObject, typeOfT: KClass<*>, context: JsonDeserializationContext): Any? {
        try {
            val cstor = typeOfT.constructors.iterator().next()
            val params = arrayOfNulls<Any>(cstor.parameters.size)

            cstor.parameters.forEach {
                it.annotations.firstOrNull { it is SerializedName }
                        ?.let { it as SerializedName }  // same with it.findAnnotation()
                        ?.value.let { jsonObject.get(it) }
                        ?: jsonObject.get(it.name)
                        ?.apply {
                            try {
                                params[it.index] = context.deserialize(this, it.type.javaType)
                            } catch (e: Exception) {
                                e.printStackTrace()
                                loge("deserialize: class:$typeOfT,cstor:$cstor,field:${it.type}", e)
                            }
                        }
            }
            return cstor.call(*params)
        } catch (e: Exception) {
            loge("deserialize: ", e)
        }
        return null
    }
}
